#ifndef __GlMatrix4x4_h__
#define __GlMatrix4x4_h__

#include <cmath>
#include <windows.h>

namespace GraphicLibrary
{
	class Vector4;

	class Matrix4x4
	{
		public:
			typedef float Array4x4[4][4];
			typedef unsigned short Index;
			typedef float Radian;

			inline Matrix4x4()	{clear();}
			inline Matrix4x4(const Matrix4x4 &source)	{operator = (source);}

			inline Matrix4x4&	operator = (const Matrix4x4 &source)	{memcpy(m, source.m, sizeof(Array4x4)); return *this;}
			inline bool		operator == (const Matrix4x4 &source) const	{return !operator != (source);}
#			pragma warning(disable: 4800)
			inline bool		operator != (const Matrix4x4 &source) const	{return memcmp(m, source.m, sizeof(Array4x4));}
#			pragma warning(default: 4800)
			inline Matrix4x4&	operator += (const Matrix4x4 &source)
			{
				m[0][0] += source.m[0][0];	m[0][1] += source.m[0][1];	m[0][2] += source.m[0][2];	m[0][3] += source.m[0][3];
				m[1][0] += source.m[1][0];	m[1][1] += source.m[1][1];	m[1][2] += source.m[1][2];	m[1][3] += source.m[1][3];
				m[2][0] += source.m[2][0];	m[2][1] += source.m[2][1];	m[2][2] += source.m[2][2];	m[2][3] += source.m[2][3];
				m[3][0] += source.m[3][0];	m[3][1] += source.m[3][1];	m[3][2] += source.m[3][2];	m[3][3] += source.m[3][3];
				return *this;
			}
			inline Matrix4x4&	operator -= (const Matrix4x4 &source)
			{
				m[0][0] -= source.m[0][0];	m[0][1] -= source.m[0][1];	m[0][2] -= source.m[0][2];	m[0][3] -= source.m[0][3];
				m[1][0] -= source.m[1][0];	m[1][1] -= source.m[1][1];	m[1][2] -= source.m[1][2];	m[1][3] -= source.m[1][3];
				m[2][0] -= source.m[2][0];	m[2][1] -= source.m[2][1];	m[2][2] -= source.m[2][2];	m[2][3] -= source.m[2][3];
				m[3][0] -= source.m[3][0];	m[3][1] -= source.m[3][1];	m[3][2] -= source.m[3][2];	m[3][3] -= source.m[3][3];
				return *this;
			}
			inline Matrix4x4&	operator *= (const Matrix4x4 &source)
			{
#				define _Mul(x, y, i)	(tmp(x, i) * source(i, y))
#				define _GetValue(x, y)	(_Mul(x, y, 0) + _Mul(x, y, 1) + _Mul(x, y, 2) + _Mul(x, y, 3))

				Matrix4x4 tmp(*this);
				at(0, 0) = _GetValue(0, 0);	at(1, 0) = _GetValue(1, 0);	at(2, 0) = _GetValue(2, 0);	at(3, 0) = _GetValue(3, 0);
				at(0, 1) = _GetValue(0, 1);	at(1, 1) = _GetValue(1, 1);	at(2, 1) = _GetValue(2, 1);	at(3, 1) = _GetValue(3, 1);
				at(0, 2) = _GetValue(0, 2);	at(1, 2) = _GetValue(1, 2);	at(2, 2) = _GetValue(2, 2);	at(3, 2) = _GetValue(3, 2);
				at(0, 3) = _GetValue(0, 3);	at(1, 3) = _GetValue(1, 3);	at(2, 3) = _GetValue(2, 3);	at(3, 3) = _GetValue(3, 3);

#				undef _Mul
#				undef _GetValue

				return *this;
			}

			inline float&		operator () (const Index x, const Index y)	{return at(x, y);}		// x - row	y - column
			inline const float operator () (const Index x, const Index y) const	{return at(x, y);}	// x - row	y - column
			inline float&		at(const Index x, const Index y)	{return m[y][x];}				// x - row	y - column
			inline const float&	at(const Index x, const Index y) const	{return m[y][x];}			// x - row	y - column
			inline void clear()
			{
				ZeroMemory(m, sizeof(Array4x4));
			}

			inline const Array4x4&	getCore() const			{return m;}
			inline const float*		asFloatPointer() const	{return (const float*)m;}

			inline void setRotationX(const Radian &rad)
			{
				clear();
				at(0, 0) = 1.0f;
				at(3, 3) = 1.0f;
				at(2, 2) = at(1, 1) = cos(rad);
				at(2, 1) = -(at(1, 2) = sin(rad));
			}
			inline void setRotationY(const Radian &rad)
			{
				clear();
				at(1, 1) = 1.0f;
				at(3, 3) = 1.0f;
				at(2, 2) = at(0, 0) = cos(rad);
				at(0, 2) = -(at(2, 0) = sin(rad));
			}
			inline void setRotationZ(const Radian &rad)
			{
				clear();
				at(2, 2) = 1.0f;
				at(3, 3) = 1.0f;
				at(1, 1) = at(0, 0) = cos(rad);
				at(1, 0) = -(at(0, 1) = sin(rad));
			}
			inline void setIdentity()
			{
				clear();
				at(0, 0) = 1.0f;
				at(1, 1) = 1.0f;
				at(2, 2) = 1.0f;
				at(3, 3) = 1.0f;
			}
			inline void setTranslocation(const float x, const float y, const float z)
			{
				setIdentity();
				at(3, 0) = x;
				at(3, 1) = y;
				at(3, 2) = z;
			}
			inline void setScale(const float x, const float y, const float z)
			{
				clear();
				at(0, 0) = x;
				at(1, 1) = y;
				at(2, 2) = z;
				at(3, 3) = 1.0f;
			}
			inline void setMirrorX()
			{
				setScale(-1.0f, 1.0f, 1.0f);
			}
			inline void setMirrorY()
			{
				setScale(1.0f, -1.0f, 1.0f);
			}
			inline void setMirrorZ()
			{
				setScale(1.0f, 1.0f, -1.0f);
			}
			inline void setPerspectiveProj(const Radian &fov, const float whRatio, const float nearClip, const float farClip)
			{
				clear();
				float eh = 1.0f / tan(fov * 0.5f);
				float ev = eh * whRatio;//1.0f / tan(fov / whRatio * 0.5f);
				float dist = farClip - nearClip;
				at(0, 0) = eh;
				at(1, 1) = ev;
				at(2, 2) = farClip / dist;
				at(2, 3) = 1;
				at(3, 2) = -at(2, 2) * nearClip;
			}
			inline void setOrthogonalProj()
			{
				clear();
				at(0, 0) = 2.0f;
				at(1, 1) = 2.0f;
				at(2, 2) = 1.0f;
				at(3, 3) = 1.0f;
				at(3, 0) = -1.0f;
				at(3, 1) = -1.0f;
			}
			inline void setCenterBasedOrthogonalProj(const Radian &fov, const float whRatio)
			{
				clear();
				float eh = 1.0f / tan(fov * 0.5f);
				float ev = eh * whRatio;//1.0f / tan(fov / whRatio * 0.5f);
				at(0, 0) = eh;
				at(1, 1) = ev;
				at(2, 2) = 1;
				at(3, 3) = 1;
			}
		private:
			Array4x4	m;
	};

	inline Vector4	operator * (const Vector4 &v, const Matrix4x4 &m);
}

#include "GlMatrix4x4.inl"

#endif	//__GlMatrix4x4_h__
